package xyz.zhzby.cyzx.judge;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import xyz.zhzby.cyzx.judge.config.ApplicationHeartbeat;
import xyz.zhzby.cyzx.judge.entity.Language;
import xyz.zhzby.cyzx.judge.service.ILanguageService;

import java.nio.charset.Charset;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@SpringBootApplication
@MapperScan("xyz.zhzby.cyzx.judge.mapper")
@Slf4j
public class JudgeApplication {

    private ApplicationContext applicationContext;

    public static void main(String[] args) {
        log.info("正在启动Judge......");
        JudgeApplication app = new JudgeApplication();
        app.getApplicationContext();
        app.setupHeartBeat();
        app.getSystemEnvironment();
        app.setUpShutdownHook();
        log.info("Judge已启动......");
    }

    private void getApplicationContext() {
        applicationContext = SpringApplication.run(JudgeApplication.class);
    }

    /**
     * @Param [] []
     * @Return void []
     * @Author zby
     * @Date 2022/02/04
     * @Description 配置与Web模块的心跳连接. 定时向Web模块发送Keep-Alive信号.
     */
    private void setupHeartBeat() {
        final int INITIAL_DELAY = 0;
        final int PERIOD = 25;

        ApplicationHeartbeat heartbeat = applicationContext.getBean(ApplicationHeartbeat.class);
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(heartbeat, INITIAL_DELAY, PERIOD, TimeUnit.MINUTES);
    }

    /**
     * @Param [] []
     * @Return void []
     * @Author zby
     * @Date 2022/02/04
     * @Description 设置ShutdownHook. 用于完成程序正常退出前的准备工作.
     */
    private void setUpShutdownHook() {
        final Thread mainThread = Thread.currentThread();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                log.info("正在关闭Judge...");
                mainThread.join();
            } catch (InterruptedException ex) {
                log.error(ex.getMessage());
            }
        }));
    }

    /**
     * @Param [] []
     * @Return void []
     * @Author zby
     * @Date 2022/02/04
     * @Description 获取系统环境变量. 以便进行Bug的复现.
     */
    private void getSystemEnvironment() {
        log.info("系统信息：");
        log.info("\t操作系统名称：" + System.getProperty("os.name"));
        log.info("\t操作系统版本：" + System.getProperty("os.version"));
        log.info("\tJava 虚拟机名称：" + System.getProperty("java.vm.name"));
        log.info("\tJava 运行环境版本：" + System.getProperty("java.runtime.version"));

        log.info("编译器信息：");
        List<Language> languages = applicationContext.getBean(ILanguageService.class).getAllLanguages();
        for (Language language : languages) {
            String languageName = language.getName();
            String compileProgram = getCompileProgram(language.getCompileCommand());
            log.info("\t" + languageName + ": " + getCompilerVersion(languageName, compileProgram));
        }
    }

    /**
     * @Param [compileCommand] [编译命令的命令行]
     * @Return java.lang.String [编译程序的命令行]
     * @Author zby
     * @Date 2022/02/04
     * @Description 获取编译程序的命令行.
     */
    private String getCompileProgram(String compileCommand) {
        int firstSpaceIndex = compileCommand.indexOf(" ");
        String compileProgram = compileCommand.substring(0, firstSpaceIndex);

        if ("javac".equalsIgnoreCase(compileProgram)) {
            return "java";
        }
        return compileProgram;
    }

    /**
     * @Param [languageName, compileProgram] [编程语言名称, 编译所使用的命令]
     * @Return java.lang.String [编译器的版本信息]
     * @Author zby
     * @Date 2022/02/04
     * @Description 获取编译器的版本信息.
     */
    private String getCompilerVersion(String languageName, String compileProgram) {
        String versionCommand = getVersionCommand(languageName);
        StringBuilder compilerVersion = new StringBuilder();

        try {
            String command = compileProgram + versionCommand;
            Process process = Runtime.getRuntime().exec(command);

            compilerVersion.append("Command Line: ").append(command).append("\n");
            compilerVersion.append(IOUtils.toString(process.getInputStream(), Charset.forName("GB2312")));
            compilerVersion.append(IOUtils.toString(process.getErrorStream(), Charset.forName("GB2312")));
        } catch (Exception ex) {
            return "Not Found";
        }
        return compilerVersion.toString();
    }

    /**
     * @Param [languageName] [编程语言名称]
     * @Return java.lang.String [获取编译器版本的命令行参数]
     * @Author zby
     * @Date 2022/02/04
     * @Description 获取编译器版本的命令行参数.
     */
    private String getVersionCommand(String languageName) {
        if ("Java".equalsIgnoreCase(languageName)) {
            return " -version";
        }
        return " --version";
    }

}
